package com.gui;

import com.swingson.SwingsonGui;
import com.swingson.SwingsonGuiBuilder;
import com.swingson.exceptions.ErrorMessageException;
import com.utils.LongRunningProcess;
import java.awt.Component;
import java.awt.HeadlessException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import org.apache.commons.io.FilenameUtils;

public class MainPanel extends JPanel {

    private final JLabel selectedFileLabel = new JLabel("No file");
    private File selectedFile;
    private final SwingsonGui gui;
    private final Preferences prefsRoot = Preferences.userRoot();
    private CodeProcessor codeProcessor;

    public MainPanel() throws HeadlessException {
        gui = SwingsonGuiBuilder.createGuiFromJsonInPackage(this);
        codeProcessor = new CodeProcessor();
        codeProcessor.loadFromFile();
    }

    public void onLoad() throws BackingStoreException {
        JFileChooser fc = new JFileChooser();
        String lastFolder = prefsRoot.get("openFolder", null);
        if (lastFolder != null) {
            fc.setCurrentDirectory(new File(lastFolder));
        }
        if (fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            gui.getDefinedComponent("processButton").getComponent().setEnabled(true);
            selectedFile = fc.getSelectedFile();
            prefsRoot.put("openFolder", selectedFile.getParent());
            prefsRoot.flush();
            selectedFileLabel.setText(selectedFile.getName());
            SwingUtilities.getWindowAncestor(this).pack();
        }
    }

    private File correctSavePath(File file, String extension) {
        String fileExtension = FilenameUtils.getExtension(file.getName());
        if (fileExtension == null || fileExtension.trim().isEmpty()) {
            return new File(file.getAbsolutePath() + "." + extension);
        }
        return file;
    }

    public void onProcess() throws FileNotFoundException, BackingStoreException {
        if (!selectedFile.exists()) {
            JOptionPane.showMessageDialog(this, "File " + selectedFile.getName() + " not found", "Error", JOptionPane.ERROR_MESSAGE);
            return;
        }
        if (!selectedFile.canRead()) {
            JOptionPane.showMessageDialog(this, "Cannot read file " + selectedFile.getName(), "Error", JOptionPane.ERROR_MESSAGE);
            return;
        }
        JFileChooser fc = new JFileChooser() {
            @Override
            public void approveSelection() {
                File f = getSelectedFile();
                if (f.exists() && getDialogType() == SAVE_DIALOG) {
                    int result = JOptionPane.showConfirmDialog(this,
                            "The file exists, overwrite?", "Existing file",
                            JOptionPane.YES_NO_CANCEL_OPTION);
                    switch (result) {
                        case JOptionPane.YES_OPTION:
                            setSelectedFile(correctSavePath(f, ((FileNameExtensionFilter) getFileFilter()).getExtensions()[0]));
                            super.approveSelection();
                            return;
                        default:
                            return;
                    }
                }

                setSelectedFile(correctSavePath(f, ((FileNameExtensionFilter) getFileFilter()).getExtensions()[0]));
                super.approveSelection();
            }
        };
        String lastFolder = prefsRoot.get("saveFolder", null);
        if (lastFolder != null) {
            fc.setCurrentDirectory(new File(lastFolder));
        }

        fc.setFileFilter(new FileNameExtensionFilter("CSV files (*.csv)", "csv"));
        fc.setAcceptAllFileFilterUsed(false);
        if (fc.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
            File file = fc.getSelectedFile();
            prefsRoot.put("saveFolder", file.getParent());
            prefsRoot.flush();

            LongRunningProcess process = new LongRunningProcess((JFrame) SwingUtilities.getWindowAncestor(this)) {

                @Override
                protected Object doInBackground() throws Exception {
                    while (!Thread.interrupted()) {
                        Thread.sleep(100);
                    }
                    return null;
                }
            };
            messageParentComponent = process.getProgressBar();

            try {
                processFile(file, selectedFile);
            } finally {
                messageParentComponent = null;
                process.cancel(true);
            }

            JOptionPane.showMessageDialog(this, "Saved to\n" + file.getAbsolutePath(), "Info", JOptionPane.INFORMATION_MESSAGE);
        }
    }

    private Component messageParentComponent;

    protected void onError(Throwable ex) {
        if (ex instanceof ErrorMessageException) {
            JOptionPane.showMessageDialog(messageParentComponent != null ? messageParentComponent : this, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
        } else {
            ex.printStackTrace();
        }
    }

    private void tryToReloadProductList() {
        codeProcessor.reloadFromServer();
    }

    public void processFile(File saveFile, File openFile) throws FileNotFoundException {
        String delimeter = ";";
        try (PrintStream outputFileStream = new PrintStream(saveFile)) {
            Map<String, Integer> barcodes;
            try (Scanner scanner = new Scanner(openFile)) {
                barcodes = new HashMap<>();
                boolean dbUpdated = false;
                while (scanner.hasNextLine()) {
                    String line = scanner.nextLine().trim();
                    line = Utils.removeUTF8BOM(line);
                    if (!line.isEmpty()) {
                        String internalRef = codeProcessor.convertBarcodeToInternalRef(line);
                        if (internalRef == null) {
                            if (dbUpdated) {
                                int result = JOptionPane.showConfirmDialog(SwingUtilities.getWindowAncestor(messageParentComponent), "Even after update, the barcode '" + line + "' is not found in the database. Skip barcode?", "Barcode still not found", JOptionPane.YES_NO_OPTION);
                                if (result == JOptionPane.YES_OPTION) {
                                    continue;
                                } else {
                                    throw new ErrorMessageException("Result file is not produced properly because barcode '" + line + "' is not found in the local and remote database.");
                                }
                            } else {
                                int result = JOptionPane.showConfirmDialog(SwingUtilities.getWindowAncestor(messageParentComponent),
                                        "The barcode '" + line + "' is not found in the database. Update database from server?\nClick Cancel to skip barcode.", "Barcode not found",
                                        JOptionPane.YES_NO_CANCEL_OPTION);
                                if (result == JOptionPane.YES_OPTION) {
                                    tryToReloadProductList();
                                    dbUpdated = true;
                                    internalRef = codeProcessor.convertBarcodeToInternalRef(line);
                                    if (internalRef == null) {
                                        result = JOptionPane.showConfirmDialog(SwingUtilities.getWindowAncestor(messageParentComponent), "Even after update, the barcode '" + line + "' is not found in the database. Skip barcode?", "Barcode still not found", JOptionPane.YES_NO_OPTION);
                                        if (result == JOptionPane.YES_OPTION) {
                                            continue;
                                        } else {
                                            throw new ErrorMessageException("Result file is not produced properly because barcode '" + line + "' is not found in the local and remote database.");
                                        }
                                    }
                                } else if (result == JOptionPane.NO_OPTION) {
                                    throw new ErrorMessageException("Result file is not produced properly because barcode '" + line + "' is not found in the local database.");
                                } else {
                                    continue;
                                }

                            }
                        }
                        if (barcodes.containsKey(internalRef)) {
                            barcodes.get(internalRef);
                            Integer counter = barcodes.get(internalRef);
                            barcodes.put(internalRef, counter + 1);
                        } else {
                            barcodes.put(internalRef, 1);
                        }

                    }
                }

                for (String barcode : barcodes.keySet()) {
                    Integer counter = barcodes.get(barcode);
                    outputFileStream.print(barcode);
                    outputFileStream.print(delimeter);
                    outputFileStream.print(counter);
                    outputFileStream.print(delimeter + " " + delimeter + " " + delimeter);
                    outputFileStream.println();
                }
            }
        }
    }
}
